import { Context, Next } from 'koa';
import { logToDatabase, LogType, LogLevel, consoleLog } from '../utils/logger';

// API 超时阈值（毫秒），超过此时间视为超时
const API_TIMEOUT_THRESHOLD = 30000; // 30秒

/**
 * 日志记录中间件
 * 记录所有 API 请求（成功、失败、超时）
 * 只记录 /api/ 开头的请求
 */
export async function loggerMiddleware(ctx: Context, next: Next): Promise<void> {
  // 只记录 API 请求
  if (!ctx.path.startsWith('/api/')) {
    await next();
    return;
  }

  const start = Date.now();
  const requestId = ctx.state.requestId || 'unknown';
  const ip = ctx.ip || ctx.request.ip || 'unknown';
  const method = ctx.method;
  const url = ctx.url;
  const requestBody = ctx.request.body;

  // 设置超时检测
  let timeoutDetected = false;
  const timeoutTimer = setTimeout(() => {
    timeoutDetected = true;
  }, API_TIMEOUT_THRESHOLD);

  try {
    await next();
    clearTimeout(timeoutTimer);
    
    // 在 next() 之后获取用户信息，此时 authMiddleware 已经执行过了
    // 尝试多种方式获取用户 ID
    let userId: string | undefined;
    
    // 方式1: 从 ctx.state.user 获取
    if (ctx.state.user) {
      userId = (ctx.state.user as any).id;
    }
    
    // 方式2: 从 ctx.state.tokenPayload 获取
    if (!userId && ctx.state.tokenPayload) {
      userId = (ctx.state.tokenPayload as any).userId;
    }
    
    // 方式3: 直接从 state 获取（兼容不同的设置方式）
    if (!userId && (ctx.state as any).user?.id) {
      userId = (ctx.state as any).user.id;
    }
    
    const duration = Date.now() - start;
    
    // 判断日志级别
    let level: LogLevel;
    let message: string;
    
    // 标记超时状态，供异常检测中间件使用
    const isTimeout = timeoutDetected || duration >= API_TIMEOUT_THRESHOLD;
    (ctx.state as any).timeoutDetected = isTimeout;
    
    if (isTimeout) {
      level = LogLevel.ERROR;
      message = `${method} ${url} - ${ctx.status} - 超时 (${duration}ms)`;
    } else if (ctx.status >= 500) {
      level = LogLevel.ERROR;
      message = `${method} ${url} - ${ctx.status} - 服务器错误 (${duration}ms)`;
    } else if (ctx.status >= 400) {
      level = LogLevel.WARN;
      message = `${method} ${url} - ${ctx.status} - 客户端错误 (${duration}ms)`;
    } else {
      level = LogLevel.INFO;
      message = `${method} ${url} - ${ctx.status} - 成功 (${duration}ms)`;
    }
    
    // 记录API日志（包含请求和响应信息）
    // 使用 try-catch 确保日志记录失败不影响请求
    try {
      await logToDatabase(
        requestId,
        LogType.API,
        level,
        message,
        {
          status: ctx.status,
          duration,
          timeout: timeoutDetected || duration >= API_TIMEOUT_THRESHOLD,
          request: {
            method,
            url,
            headers: ctx.request.headers,
            body: requestBody,
            query: ctx.query,
          },
          response: {
            status: ctx.status,
            body: ctx.body,
          },
        },
        userId,
        ip,
        method,
        url
      );
      // 开发环境下打印日志记录成功的提示（包含用户ID信息）
      if (process.env.NODE_ENV === 'dev' && 
          ctx.path.startsWith('/api/') && 
          !ctx.path.startsWith('/api/auth/login') &&
          !ctx.path.startsWith('/api/health')) {
        // 合并用户ID信息和已记录到数据库的提示
        const userInfo = userId 
          ? `用户ID: ${userId}` 
          : '未获取到用户ID';
        const extraInfo: any = {
          requestId,
          method,
          url,
          ip,
        };
        
        if (userId) {
          extraInfo.userId = userId;
        } else {
          // 如果未获取到用户ID，添加调试信息
          extraInfo.stateUser = ctx.state.user ? '存在' : '不存在';
          extraInfo.stateTokenPayload = ctx.state.tokenPayload ? '存在' : '不存在';
          if (ctx.state.tokenPayload) {
            extraInfo.tokenPayloadUserId = (ctx.state.tokenPayload as any).userId;
          }
        }
        
        consoleLog(
          LogType.API,
          LogLevel.DEBUG,
          `${method} ${url} - ${userInfo} - 已记录到数据库`,
          extraInfo
        );
      }
    } catch (logError: any) {
      // 日志记录失败不影响主流程，只打印到控制台
      consoleLog(
        LogType.API,
        LogLevel.ERROR,
        'API日志记录失败',
        {
          requestId,
          method,
          url,
          ip,
          error: logError,
        }
      );
    }
  } catch (err: any) {
    clearTimeout(timeoutTimer);
    const duration = Date.now() - start;
    
    // 标记错误状态，供异常检测中间件使用
    (ctx.state as any).hasError = true;
    
    // 在错误处理时也获取用户信息（此时 authMiddleware 可能已经执行过了）
    // 尝试多种方式获取用户 ID
    let userId: string | undefined;
    
    // 方式1: 从 ctx.state.user 获取
    if (ctx.state.user) {
      userId = (ctx.state.user as any).id;
    }
    
    // 方式2: 从 ctx.state.tokenPayload 获取
    if (!userId && ctx.state.tokenPayload) {
      userId = (ctx.state.tokenPayload as any).userId;
    }
    
    // 方式3: 直接从 state 获取（兼容不同的设置方式）
    if (!userId && (ctx.state as any).user?.id) {
      userId = (ctx.state as any).user.id;
    }
    
    // 判断是否是超时错误
    const errWithCode = err as Error & { code?: string };
    const isTimeout = errWithCode.code === 'ETIMEDOUT' || err.message?.includes('timeout') || timeoutDetected;
    (ctx.state as any).timeoutDetected = isTimeout;
    
    // 记录错误日志
    // 使用 try-catch 确保日志记录失败不影响错误处理
    try {
      await logToDatabase(
        requestId,
        LogType.ERROR,
        LogLevel.ERROR,
        `API错误: ${err.message || 'Unknown error'}${isTimeout ? ' (超时)' : ''}`,
        {
          error: {
            name: err.name,
            message: err.message,
            stack: err.stack,
          },
          status: ctx.status || 500,
          duration,
          timeout: isTimeout,
          request: {
            method,
            url,
            headers: ctx.request.headers,
            body: requestBody,
            query: ctx.query,
          },
        },
        userId,
        ip,
        method,
        url
      );
    } catch (logError) {
      // 日志记录失败不影响错误处理，只打印到控制台
      consoleLog(
        LogType.ERROR,
        LogLevel.ERROR,
        'API错误日志记录失败',
        {
          requestId,
          userId,
          method,
          url,
          ip,
          error: logError,
        }
      );
    }
    
    throw err;
  }
}

